/*
 * Translated from the Koprina project, a Java implementation of the Skype SILK codec
 * http://code.google.com/p/koprina/
 */

using System;

namespace SilkSharp
{
	class Silk_macros
	{
		public static int TRIPLE_SHIFT( int i, int shift )
		{
			return (int)( (uint)i >> shift );
		}

		// (a32 * (SKP_int32)((SKP_int16)(b32))) >> 16 output have to be 32bit int
		public static int SKP_SMULWB( int a32, int b32 )
		{
			return ( ( ( ( a32 ) >> 16 ) * (int)( (short)( b32 ) ) ) + ( ( ( ( a32 ) & 0x0000FFFF ) * (int)( (short)( b32 ) ) ) >> 16 ) );
		}

		// a32 + (b32 * (SKP_int32)((SKP_int16)(c32))) >> 16 output have to be 32bit int
		public static int SKP_SMLAWB( int a32, int b32, int c32 )
		{
			return ( ( a32 ) + ( ( ( ( b32 ) >> 16 ) * (int)( (short)( c32 ) ) ) + ( ( ( ( b32 ) & 0x0000FFFF ) * (int)( (short)( c32 ) ) ) >> 16 ) ) );
		}

		// (a32 * (b32 >> 16)) >> 16
		public static int SKP_SMULWT( int a32, int b32 )
		{
			return ( ( ( a32 ) >> 16 ) * ( ( b32 ) >> 16 ) + ( ( ( ( a32 ) & 0x0000FFFF ) * ( ( b32 ) >> 16 ) ) >> 16 ) );
		}

		// a32 + (b32 * (c32 >> 16)) >> 16
		public static int SKP_SMLAWT( int a32, int b32, int c32 )
		{
			return ( ( a32 ) + ( ( ( b32 ) >> 16 ) * ( ( c32 ) >> 16 ) ) + ( ( ( ( b32 ) & 0x0000FFFF ) * ( ( c32 ) >> 16 ) ) >> 16 ) );
		}

		// (SKP_int32)((SKP_int16)(a3))) * (SKP_int32)((SKP_int16)(b32)) output have to be 32bit int
		public static int SKP_SMULBB( int a32, int b32 )
		{
			return ( (int)( (short)( a32 ) ) * (int)( (short)( b32 ) ) );
		}

		// a32 + (SKP_int32)((SKP_int16)(b32)) * (SKP_int32)((SKP_int16)(c32)) output have to be 32bit int
		public static int SKP_SMLABB( int a32, int b32, int c32 )
		{
			return ( ( a32 ) + ( (int)( (short)( b32 ) ) ) * (int)( (short)( c32 ) ) );
		}

		// (SKP_int32)((SKP_int16)(a32)) * (b32 >> 16)
		public static int SKP_SMULBT( int a32, int b32 )
		{
			return ( (int)( (short)( a32 ) ) * ( ( b32 ) >> 16 ) );
		}

		// a32 + (SKP_int32)((SKP_int16)(b32)) * (c32 >> 16)
		public static int SKP_SMLABT( int a32, int b32, int c32 )
		{
			return ( ( a32 ) + ( (int)( (short)( b32 ) ) ) * ( ( c32 ) >> 16 ) );
		}

		// a64 + (b32 * c32)
		public static long SKP_SMLAL( long a64, int b32, int c32 )
		{
			return a64 + (long)b32 * (long)c32;
		}

		// (a32 * b32) >> 16
		public static int SKP_SMULWW( int a32, int b32 )
		{
			return SKP_SMULWB( a32, b32 ) + a32 * Silk_SigProc_FIX.SKP_RSHIFT_ROUND( b32, 16 );
		}

		// a32 + ((b32 * c32) >> 16)
		public static int SKP_SMLAWW( int a32, int b32, int c32 )
		{
			return SKP_SMLAWB( a32, b32, c32 ) + b32 * Silk_SigProc_FIX.SKP_RSHIFT_ROUND( c32, 16 );
		}

		/* add/subtract with output saturated */
		public static int SKP_ADD_SAT32( int a, int b )
		{
			if( ( ( a + b ) & 0x80000000 ) == 0 )
				return ( ( a & b ) & 0x80000000 ) != 0 ? int.MinValue : a + b;
			else
				return ( ( a | b ) & 0x80000000 ) == 0 ? int.MaxValue : a + b;
		}

		public static int SKP_SUB_SAT32( int a, int b )
		{
			if( ( ( a - b ) & 0x80000000 ) == 0 )
				return ( a & ( b ^ 0x80000000 ) & 0x80000000 ) != 0 ? int.MinValue : a - b;
			else
				return ( ( a ^ 0x80000000 ) & b & 0x80000000 ) != 0 ? int.MaxValue : a - b;
		}

		public static int SKP_Silk_CLZ16( short in16 )
		{
			return LeadingZeros( (int)in16 & 0x0000FFFF ) - 16;
		}

		public static int SKP_Silk_CLZ32( int in32 )
		{
			return LeadingZeros( in32 );
		}

		public static void FillArray<T>( T[] sourceArray, T value )
		{
			FillArray<T>( sourceArray, 0, sourceArray.Length - 1, value );
		}

		public static void FillArray<T>( T[] sourceArray, int startIndex, int endIndex, T value )
		{
			for( int i = startIndex; i <= endIndex; i++ )
			{
				sourceArray[ i ] = value;
			}
		}

		public static T[] copyOfRange<T>( T[] sourceArray, int from, int to )
		{
			int numElements = ( to - from ) + 1;
			T[] arr = new T[ numElements ];
			for( int i = 0; i < numElements; i++ )
			{
				arr[ i ] = sourceArray[ i + from ];
			}
			return arr;
		}

		public static int LeadingZeros( long x )
		{
			long firstHalf = x >> 32;
			int firstHalfLeadingZeroes = LeadingZeros( (int)firstHalf );
			if( firstHalfLeadingZeroes < 32 )
				return firstHalfLeadingZeroes;
			else
			{
				int secondHalfLeadingZeroes = LeadingZeros( (int)x );
				return firstHalfLeadingZeroes + secondHalfLeadingZeroes;
			}
		}

		public static int LeadingZeros( int x )
		{
			x |= ( x >> 1 );
			x |= ( x >> 2 );
			x |= ( x >> 4 );
			x |= ( x >> 8 );
			x |= ( x >> 16 );
			return ( sizeof( int ) * 8 - Ones( x ) );
		}

		/*
		 * This method helps me make a jagged array in fewer lines.
		 * This is because the guys who made C# apparently didn't think accessing
		 * a multidimensional array as if it were a jagged array (like x[0] which returns a flat array) would be, oh, I don't know, useful?
		 * They also didn't think anybody would ever want to use a jagged array like a multidimensional array.
		 * Argh.
		 */
		public static T[][] MakeJaggedArray<T>( int w, int h )
		{
			T[][] arr = new T[ w ][];
			for( int i = 0; i < w; i++ )
			{
				arr[ i ] = new T[ h ];
			}

			return arr;
		}

		private static int Ones( int x )
		{
			x -= ( ( x >> 1 ) & 0x55555555 );
			x = ( ( ( x >> 2 ) & 0x33333333 ) + ( x & 0x33333333 ) );
			x = ( ( ( x >> 4 ) + x ) & 0x0f0f0f0f );
			x += ( x >> 8 );
			x += ( x >> 16 );
			return ( x & 0x0000003f );
		}
	}
}
